package com.yxsk.relay.job.endpoint.combine.auto.config.components;

import com.yxsk.relay.job.component.common.utils.CollectionUtils;
import com.yxsk.relay.job.component.endpoint.exception.bean.JobHandlerBeanException;
import com.yxsk.relay.job.component.endpoint.handle.JobExecutor;
import com.yxsk.relay.job.component.endpoint.handle.anno.JobHandler;
import com.yxsk.relay.job.component.endpoint.handle.context.RelayJobExecutorManager;
import com.yxsk.relay.job.component.endpoint.utils.ClassScanUtils;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;

import java.lang.annotation.*;
import java.text.MessageFormat;
import java.util.List;
import java.util.stream.Stream;

/**
 * @Description
 * @Author 11376
 * @CreateTime 2019/9/16 15:46
 */
@Configuration
@AutoConfigureOrder(3)
public class JobExecutorContextConfig implements ApplicationContextAware {

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        RelayJobExecutorManager.setExecutorBeanFactory(new SpringBootJobHandleBeanFactory(applicationContext));
    }

    @AllArgsConstructor
    private static class SpringBootJobHandleBeanFactory implements RelayJobExecutorManager.JobExecutorBeanFactory {

        private ApplicationContext applicationContext;

        @Override
        public JobExecutor createInstance(Class<? extends JobExecutor> executorClass, JobHandler.Scope scope) throws JobHandlerBeanException {
            return applicationContext.getBean(executorClass);
        }
    }

    /**
     * @Author 1137
     * @Description 任务执行器 spring 容器、 relay-job任务执行管理器 注册器
     * @Date
     */
    public static class JobExecutorAnnotationRegistrar implements ImportBeanDefinitionRegistrar {

        private static BeanDefinitionRegistry beanDefinitionRegistry;

        private static String[] basePackages;

        private static Class<?>[] classes;

        @Override
        public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
            AnnotationAttributes attributes = AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(RelayJobHandlerAutoScan.class.getName()));
            String[] basePackages = attributes.getStringArray("basePackages");
            Class<?>[] classes = attributes.getClassArray("classes");

            JobExecutorAnnotationRegistrar.basePackages = basePackages;
            JobExecutorAnnotationRegistrar.classes = classes;

            JobExecutorAnnotationRegistrar.beanDefinitionRegistry = beanDefinitionRegistry;
            registryJobExecutor();
        }

        static void registryJobExecutor() {
            // 开始扫描
            if (JobExecutorAnnotationRegistrar.basePackages != null) {
                Stream.of(JobExecutorAnnotationRegistrar.basePackages).forEach(basePackage -> {
                    List<Class<?>> executorClasses = ClassScanUtils.scanClasses(basePackage, JobExecutor.class);
                    if (CollectionUtils.isNotEmpty(executorClasses)) {
                        // 注册 bean
                        executorClasses.stream().forEach(executorClass -> {
                            JobHandler annotation = executorClass.getAnnotation(JobHandler.class);
                            if (annotation != null) {
                                registryBean(beanDefinitionRegistry, executorClass, annotation.value());
                            }
                        });
                    }
                });
            }

            // 注入类
            if (JobExecutorAnnotationRegistrar.classes != null) {
                Stream.of(JobExecutorAnnotationRegistrar.classes).forEach(clazz -> {
                    JobHandler annotation = clazz.getAnnotation(JobHandler.class);
                    if (annotation == null) {
                        throw new IllegalArgumentException(MessageFormat.format("No jobhandle annotation found on class {0}", clazz.getName()));
                    }
                    registryBean(beanDefinitionRegistry, clazz, annotation.value());
                });
            }
        }

        private static void registryBean(BeanDefinitionRegistry registry, Class executorClass, String handleName) {
            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
            beanDefinition.setBeanClass(executorClass);
            BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, handleName);
            BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);

            RelayJobExecutorManager.load(new Class[]{executorClass});
        }

    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(JobExecutorAnnotationRegistrar.class)
    public @interface RelayJobHandlerAutoScan {

        String[] basePackages() default {};

        Class<? extends JobExecutor>[] classes() default {};

    }

}
